# coding:utf8

import socket, Queue, select, traceback, time
'''
chat server implemented with select
'''
class ChatServer():
    def __init__(self):
        self.dict_name = {}
        self.dict_msg = {}
        self.listen_fd = 0
        self.inputs = []
        self.outputs = []

    def run(self):
        ''' listen & select '''
        s = socket.socket()
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind(('localhost', 10001))
        s.listen(1)
        self.inputs.append(s)
        self.listen_fd = s
        while True:
            list_r, list_w, list_e = select.select(self.inputs, self.outputs, self.inputs)
            #accept function
            if self.listen_fd in list_r:
                conn, cli = self.listen_fd.accept()
                self.dict_name[conn] = ''
                self.dict_msg[conn] = Queue.Queue()
                self.inputs.append(conn)
                print 'new connects', conn.getpeername()
            self.doExcept(list_e)
            self.doRead(list_r)
            self.doWrite(list_w)
            time.sleep(0.1)

    def doRead(self, list_r=[]):
        for ts in list_r:
            if ts is self.listen_fd: continue
            try:
                msg = ts.recv(1024)
                if msg:
                    print 'read[%s]' % msg 
                    cmd, tmp = msg.split(None, 1)
                    print 'split[%s][%s]' % (cmd, tmp)
                    if cmd == 'name':
                        self.doName(ts,tmp)
                    elif cmd == 'pm':
                        self.doPm(ts, tmp)
                    elif cmd == 'show':
                        self.doShow(ts, tmp)
                    else:
                        self.doMsg(ts, tmp)
                    if ts not in self.outputs:
                        self.outputs.append(ts)
                else:
                    self.doExcept([ts])
                print 'read', ts.fileno(), len(msg)
            except:
                traceback.print_exc()

    def doWrite(self, list_w=[]):
        for ts in list_w:
            try:
                if not self.dict_msg[ts].empty():
                    msg = self.dict_msg[ts].get_nowait()
                    if msg:
                        ts.send(msg)
                    print 'write', ts.fileno(), len(msg)
            except:
                traceback.print_exc()

    def doExcept(self, list_e=[]):
        for ts in list_e:
            if ts in self.inputs:
                self.inputs.remove(ts)
            if ts in self.outputs:
                self.outputs.remove(ts)
            if ts in self.dict_name:
                del self.dict_name[ts]
            if ts in self.dict_msg:
                del self.dict_msg[ts]
            print 'except', ts.fileno()

    def doName(self, ts='', tmp=''):
        self.dict_name[ts] = tmp
        users = [k for k in self.dict_name if self.dict_name[k]]
        for s in self.dict_msg:
            self.dict_msg[s].put('welcome %s, there are %s friends now' % (tmp, len(users)))


    def doMsg(self, ts='', msg=''):
        name = self.dict_name[ts]
        for s in self.dict_msg:
            self.dict_msg[s].put('from %s: %s' % (name, msg))

    def doShow(self, ts='', msg=''):
        users = [self.dict_name[k] for k in self.dict_name if self.dict_name[k]]
        if ts in self.dict_msg:
            msg = 'there are %s users\n %s' % (len(users), users)
            self.dict_msg[ts].put(msg)

    def doPm(self, ts='', msg=''):
        user, tmp = msg.split(None, 1)
        find_user = 0
        for s in self.dict_name:
            if self.dict_name[s] == user:
                self.dict_msg[s].put('from %s:%s' % (self.dict_name[ts], tmp))
                find_user = 1
                break
        if not find_user:
            self.dict_msg[ts].put('user [%s] not found, and private msg not sent successfully' % find_user)

if __name__=='__main__':
    cs = ChatServer()
    cs.run()